package editor.modelo;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;

import editor.graphics.Dibujable;

public class EditorPortal extends Portal implements Dibujable {

	private static final int PORTAL_SIZE = 4;
	private static final Color COLOR_PORTAL = new Color(200, 250, 200, 40);
	private static final Color COLOR_PORTAL_SELECCIONADO = new Color(200, 250, 200, 100);

	Polygon arrowPolygon = new Polygon();

	public EditorPortal() {
		this.tipo = Tipo.values()[0];
		this.clase = Clase.values()[0];

		arrowPolygon.addPoint(0, 2);
		arrowPolygon.addPoint(8, 2);
		arrowPolygon.addPoint(8, 4);

		arrowPolygon.addPoint(12, 0);

		arrowPolygon.addPoint(8, -4);
		arrowPolygon.addPoint(8, -2);
		arrowPolygon.addPoint(0, -2);
	}

	public float getWidth() {

		return (float) ptoSource.distance(source.getNextPoint(ptoSource));
	}

	public float getLength() {
		float par1 = (float) Math.min(ptoSource.distance(ptoTarget),
				ptoSource.distance(this.target.getNextPoint(ptoTarget)));
		float par2 = (float) Math.min(
				source.getNextPoint(ptoSource).distance(ptoTarget),
				source.getNextPoint(ptoSource).distance(
						this.target.getNextPoint(ptoTarget)));
		return Math.max(par1, par2);
	}

	public void fitRelativeToSource(float width, float length) {
		Point2D parSource, parNext, vector, perpend;
		if (ptoSource.distance(ptoTarget) <= ptoSource.distance(this.target
				.getNextPoint(ptoTarget))) {
			parSource = ptoTarget;
			parNext = target.getNextPoint(ptoTarget);
		} else {
			parSource = target.getNextPoint(ptoTarget);
			parNext = ptoTarget;
		}

		vector = getDireccion(ptoSource, source.getNextPoint(ptoSource));

		perpend = getPerpendicularLejano(vector, ptoSource, source.getCentro());
		// Tengo que elgegir que perpendicular uso. ->el que este mas lejos del
		// source center

		parSource.setLocation(ptoSource.getX() + perpend.getX() * length,
				ptoSource.getY() + perpend.getY() * length);

		source.getNextPoint(ptoSource).setLocation(
				ptoSource.getX() + vector.getX() * width,
				ptoSource.getY() + vector.getY() * width);
		parNext.setLocation(parSource.getX() + vector.getX() * width,
				parSource.getY() + vector.getY() * width);

	}

	public Point2D getPerpendicularLejano(Point2D vector, Point2D ref,
			Point2D ptoInterno) {
		Point2D.Double perp = new Point2D.Double(vector.getY(), -vector.getX());
		double dist = perp.distance(0, 0);

		Point2D.Double aux = new Point2D.Double(ref.getX() + perp.getX(),
				ref.getY() + perp.getY());

		if (aux.distance(ptoInterno) >= ref.distance(ptoInterno)) {

			perp.setLocation(perp.getX() / dist, perp.getY() / dist);
		} else {
			perp.setLocation(-perp.getX() / dist, -perp.getY() / dist);

		}
		return perp;

	}

	public Point2D getDireccion(Point2D de, Point2D hasta) {
		Point2D.Double p = new Point2D.Double(hasta.getX() - de.getX(),
				hasta.getY() - de.getY());
		double dist = p.distance(0, 0);
		p.setLocation(p.getX() / dist, p.getY() / dist);
		return p;
	}

	public boolean containsPoint(Point2D p) {
		int x, y, x2, y2;
		if (clase == Clase.inter_floor || target == null) {
			return p.distance(ptoSource) < 	PORTAL_SIZE;
		} else {
			x = (int) getMin((float) ptoSource.getX(),
					(float) ptoTarget.getX(),
					(float) source.getNextPoint(ptoSource).getX(),
					(float) target.getNextPoint(ptoTarget).getX());

			if (p.getX() < x)
				return false;

			y = (int) getMin((float) ptoSource.getY(),
					(float) ptoTarget.getY(),
					(float) source.getNextPoint(ptoSource).getY(),
					(float) target.getNextPoint(ptoTarget).getY());

			if (p.getY() < y)
				return false;

			x2 = (int) getMax((float) ptoSource.getX(),
					(float) ptoTarget.getX(),
					(float) source.getNextPoint(ptoSource).getX(),
					(float) target.getNextPoint(ptoTarget).getX());

			if (p.getX() > x2)
				return false;

			y2 = (int) getMax((float) ptoSource.getY(),
					(float) ptoTarget.getY(),
					(float) source.getNextPoint(ptoSource).getY(),
					(float) target.getNextPoint(ptoTarget).getY());

			if (p.getY() > y2)
				return false;

			return true;
		}

	}

	private float getMin(float p1, float p2, float p3, float p4) {
		float x = Math.min(p1, p2);
		x = Math.min(x, p3);
		x = Math.min(x, p4);
		return x;
	}

	private float getMax(float p1, float p2, float p3, float p4) {
		float x = Math.max(p1, p2);
		x = Math.max(x, p3);
		x = Math.max(x, p4);
		return x;
	}

	public void dibujar(Graphics2D g) {
	    g.setFont(new Font("TimesRoman", Font.PLAIN, 6));
		g.setColor(COLOR_PORTAL);
		if (clase == Clase.inter_floor) {
			dibujarExternal(g);
		} else {
			if(target!= null)
				dibujarInterno(g, false);
				else dibujarExternal(g);
		}
	}

	public void dibujarSelccionado(Graphics2D g) {
		g.setColor(COLOR_PORTAL_SELECCIONADO);
		if (clase == Clase.inter_floor) {
			dibujarExternal(g);
		} else {
			if(target!= null)
			dibujarInterno(g, true);
			else dibujarExternal(g);
		}
	}

	private void dibujarInterno(Graphics2D g, boolean seleccionado) {
		int x, y, x2, y2;
		x = (int) getMin((float) ptoSource.getX(), (float) ptoTarget.getX(),
				(float) source.getNextPoint(ptoSource).getX(), (float) target
						.getNextPoint(ptoTarget).getX());

		y = (int) getMin((float) ptoSource.getY(), (float) ptoTarget.getY(),
				(float) source.getNextPoint(ptoSource).getY(), (float) target
						.getNextPoint(ptoTarget).getY());

		x2 = (int) getMax((float) ptoSource.getX(), (float) ptoTarget.getX(),
				(float) source.getNextPoint(ptoSource).getX(), (float) target
						.getNextPoint(ptoTarget).getX());

		y2 = (int) getMax((float) ptoSource.getY(), (float) ptoTarget.getY(),
				(float) source.getNextPoint(ptoSource).getY(), (float) target
						.getNextPoint(ptoTarget).getY());

		if (seleccionado) {
			g.setColor(Color.orange);
			g.fillRect(x - PORTAL_SIZE/2, y - PORTAL_SIZE/2, x2 - x +  PORTAL_SIZE, y2 - y +  PORTAL_SIZE);
		
			g.drawRect(x - PORTAL_SIZE/2, y - PORTAL_SIZE/2, x2 - x +  PORTAL_SIZE, y2 - y +  PORTAL_SIZE);
		} else {
			g.fillRect(x - 1, y - 1, x2 - x + 2, y2 - y + 2);
			g.setColor(Color.GREEN);
			g.drawRect(x - 1, y - 1, x2 - x + 2, y2 - y + 2);
		}
		if(this.tipo != null){
			g.drawString(this.tipo.toString(),(int)x -5 ,(int)y-10);
			if(this.tipo == Tipo.dstair || this.tipo == Tipo.ustair){
				g.drawString("(" + String.valueOf(this.getDifferential())+ ")",(int)x -5 ,(int)y -5);
			}
		}
		drawArrowFrom(source, ptoSource, g);
		if (this.isBidirectional()) {
			drawArrowFrom(target, ptoTarget, g);
		}
	}

	private void dibujarExternal(Graphics2D g) {
		int x, y, x2, y2;

		if(ptoSource == null || source.getNextPoint(ptoSource) == null)
			return;
		//	System.out.println(this.getSource().getName());
		x = (int) Math.min((float) ptoSource.getX(), (float) source
				.getNextPoint(ptoSource).getX());

		y = (int) Math.min((float) ptoSource.getY(), (float) source
				.getNextPoint(ptoSource).getY());

		x2 = (int) Math.max((float) ptoSource.getX(), (float) source
				.getNextPoint(ptoSource).getX());

		y2 = (int) Math.max((float) ptoSource.getY(), (float) source
				.getNextPoint(ptoSource).getY());

		if(this.getTarget()!= null)
			g.setColor(new Color (0,0,200, 100));
		else
			g.setColor(new Color(255, 0, 0, 150));
		g.fillRect(x - PORTAL_SIZE/2, y - PORTAL_SIZE/2, x2 - x +  PORTAL_SIZE, y2 - y +  PORTAL_SIZE);
		g.drawRect(x - PORTAL_SIZE/2, y - PORTAL_SIZE/2, x2 - x +  PORTAL_SIZE, y2 - y +  PORTAL_SIZE);
		if(this.tipo != null){
			g.drawString(this.tipo.toString(),(int)x -5 ,(int)y-4 );
		}
		drawArrowFrom(source, ptoSource, g);
	
	}

	private void drawArrowFrom(Space s, Point2D p, Graphics2D g) {
		Point2D vector = getDireccion(p, s.getNextPoint(p));
		Point2D mid = midpoint(p, s.getNextPoint(p));
		Point2D perpend = getPerpendicularLejano(vector, p, s.getCentro());
		// Tengo que elgegir que perpendicular uso. ->el que este mas lejos del
		// source center

		Point2D hacia = new Point2D.Float(
				(float) (mid.getX() + (perpend.getX() * 8)),
				(float) (mid.getY() + perpend.getY() * 8));

		g.setColor(new Color(250, 220, 220, 220));
		g.fill(createArrowShape(mid, hacia));
	}

	public Shape createArrowShape(Point2D fromPt, Point2D toPt) {
		Point2D midPoint = midpoint(fromPt, toPt);
		double rotate = Math.atan2(toPt.getY() - fromPt.getY(), toPt.getX()
				- fromPt.getX());
		AffineTransform transform = new AffineTransform();
		transform.translate(midPoint.getX(), midPoint.getY());
		// double ptDistance = fromPt.distance(toPt);
		// double scale = ptDistance / 12.0; // 12 because it's the length of
		// the arrow polygon.
		//transform.scale(2, 2);
		transform.rotate(rotate);
		return transform.createTransformedShape(arrowPolygon);
	}

	private static Point2D midpoint(Point2D fromPt, Point2D toPt) {
		return new Point2D.Float((int) ((fromPt.getX() + toPt.getX()) / 2.0),
				(int) ((fromPt.getY() + toPt.getY()) / 2.0));
	}

}
